package com.oa.official.common.exception;
import com.oa.official.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.ValidationException;
import java.io.IOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.file.AccessDeniedException;
import java.sql.SQLException;
import java.util.List;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 全局系统异常，未知异常统一处理
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = Exception.class)
    public Result errorHandler(Exception e, HttpServletRequest request) {
        if (e instanceof NoHandlerFoundException) {
            log.error("NoHandlerFoundException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("请求找不到请求资源控制器",HttpStatus.NOT_FOUND.value());
        } else if (e instanceof MissingServletRequestParameterException) {
            log.error("MissingServletRequestParameterException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("缺少请求参数");
        } else if (e instanceof TypeMismatchException) {
            log.error("TypeMismatchException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("参数类型匹配失败");
        } else if(e instanceof HttpMessageNotReadableException){
            log.error("HttpMessageNotReadableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("json数据序列化为对象时发生错误");
        } else if(e instanceof HttpMediaTypeNotSupportedException){
            log.error("HttpMediaTypeNotSupportedException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("请求Content-type类型不支持");
        } else if(e instanceof HttpMessageNotWritableException){
            log.error("HttpMessageNotWritableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("对象序列化为json时发生错误");
        } else if(e instanceof HttpRequestMethodNotSupportedException){
            log.error("HttpRequestMethodNotSupportedException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("请求方式不支持");
        } else if(e instanceof MissingPathVariableException){
            log.error("MissingPathVariableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("未检测到路径参数");
        } else if(e instanceof MethodArgumentNotValidException){
            String errorMsg = getErrorMsg((MethodArgumentNotValidException) e, request);
            return Result.error(errorMsg);
        } else if(e instanceof BindException){
            log.error("BindException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("请求参数绑定异常");
        } else if(e instanceof AuthenticationException){
            log.error("AuthenticationException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("认证失败,请重新登陆",HttpStatus.FORBIDDEN.value());
        } else if(e instanceof AccessDeniedException){
            log.error("AccessDeniedException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("权限不足，请联系管理员!",HttpStatus.UNAUTHORIZED.value());
        } else if(e instanceof ValidationException){
            log.error("validationException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR.value());
        } else if (e instanceof SQLException) {
            log.error("SQLException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("数据库操作失败");
        } else if (e instanceof MaxUploadSizeExceededException) {
            log.error("MaxUploadSizeExceededException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("文件上传超出大小限制");
        } else if (e instanceof SocketTimeoutException) {
            log.error("SocketTimeoutException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("服务连接超时",HttpStatus.REQUEST_TIMEOUT.value());
        } else if (e instanceof SocketException) {
            log.error("SocketException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("服务连接失败");
        } else if(e instanceof CustomException){
            log.error("CustomException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error(e.getMessage());
        } else if(e instanceof RuntimeException){
            log.error("RuntimeException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("系统错误");
        } else if (e instanceof IOException) {
            log.error("IOException error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("文件IO读写错误");
        } else {
            log.error("Exception error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return Result.error("系统错误");
        }
    }

    @NotNull
    private static String getErrorMsg(MethodArgumentNotValidException e, HttpServletRequest request) {
        BindingResult result = e.getBindingResult();
        StringBuilder stringBuilder = new StringBuilder("参数校验失败:");
        List<ObjectError> errors = result.getAllErrors();
        errors.forEach(p -> {
            FieldError fieldError = (FieldError) p;
            log.error("MethodArgumentNotValidException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            stringBuilder.append(":").append(fieldError.getDefaultMessage()).append(";");
        });
        return stringBuilder.toString();
    }

}